﻿#include "pch.h"
#include "Cheater.h"
#include "Memory.h"
#include "GlobalVars.h"
#include <fstream>
#include <iostream>
#include <thread>
#include <chrono>
#include "Renderer.h"

void init();
void CreateRenderTarget();
void CleanupRenderTarget();
void CleanupDeviceD3D();
bool CreateDeviceD3D();
void initOverlayWindow();
void getGameWindowRect();
LRESULT WINAPI WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Forward declare message handler from imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Win32消息处理
LRESULT WINAPI WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
	{
		return true;
	}

	switch (msg)
	{
		case WM_SIZE:
			if (Renderer::get().pD3DDevice != NULL && wParam != SIZE_MINIMIZED)
			{
				CleanupRenderTarget();
				Renderer::get().pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
				CreateRenderTarget();
			}
			return false;
			break;
		case WM_SYSCOMMAND:
			if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
				return false;
			break;

		case WM_DESTROY:
			::PostQuitMessage(0);
			return false;
			break;
	}
	return ::DefWindowProc(hWnd, msg, wParam, lParam);
}

void CreateRenderTarget()
{
	ID3D11Texture2D * pBackBuffer;
	Renderer::get().pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
	Renderer::get().pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &Renderer::get().pMainRenderTargetView);
	pBackBuffer->Release();
}

void CleanupRenderTarget()
{
	if (Renderer::get().pMainRenderTargetView)
	{
		Renderer::get().pMainRenderTargetView->Release();
		Renderer::get().pMainRenderTargetView = nullptr;
	}
}

void CleanupDeviceD3D()
{
	CleanupRenderTarget();
	if (Renderer::get().pSwapChain)
	{
		Renderer::get().pSwapChain->Release();
		Renderer::get().pSwapChain = nullptr;
	}
	if (Renderer::get().pD3DDeviceContext)
	{
		Renderer::get().pD3DDeviceContext->Release();
		Renderer::get().pD3DDeviceContext = nullptr;
	}
	if (Renderer::get().pD3DDevice)
	{
		Renderer::get().pD3DDevice->Release();
		Renderer::get().pD3DDevice = nullptr;
	}
}

// 初始化D3D
bool CreateDeviceD3D()
{
	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(sd));
	sd.BufferCount = 2;
	sd.BufferDesc.Width = 0;
	sd.BufferDesc.Height = 0;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = GlobalVars::get().overlayHWindow;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;
	sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	UINT createDeviceFlags = 0;
	D3D_FEATURE_LEVEL featureLevel;
	const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
	if (D3D11CreateDeviceAndSwapChain(NULL,
									  D3D_DRIVER_TYPE_HARDWARE,
									  NULL,
									  createDeviceFlags,
									  featureLevelArray,
									  2,
									  D3D11_SDK_VERSION,
									  &sd,
									  &Renderer::get().pSwapChain,
									  &Renderer::get().pD3DDevice,
									  &featureLevel,
									  &Renderer::get().pD3DDeviceContext) != S_OK)
	{
		return false;
	}

	CreateRenderTarget();
	cout << "创建D3D成功 " << endl;
	return true;
}

// 获取游戏窗口尺寸
void getGameWindowRect()
{
	// Rect
	// 左是矩形左边界离屏幕最左边的距离
	// 上是矩形上边界离屏幕最上边的距离
	// 右是矩形右边界离屏幕最左边的距离
	// 下是矩形下边界离屏幕最上边的距离
	// 这里获取到的尺寸，是加上了被Windows缩放的部分
	// 比如我屏幕设置缩放150%，那我游戏设置为1280*720，GetClientRect最终获取到的数值是(1280*1.5)*(720*1.5)=1920*1080
	RECT marginRect;
	RECT overlayRect;
	GetWindowRect(GlobalVars::get().hWindow, &marginRect);
	GetClientRect(GlobalVars::get().hWindow, &overlayRect);

	// 窗口模式，窗口左边的边框尺寸
	int winBorderLeft = ((marginRect.right - marginRect.left) - (overlayRect.right - overlayRect.left)) * 0.5;
	// 窗口模式，窗口顶部的边框尺寸，也就是窗口标题栏高度
	int winBorderTop = (marginRect.bottom - marginRect.top) - (overlayRect.bottom - overlayRect.top) - winBorderLeft;

	int clientCenterX = (overlayRect.right - overlayRect.left) * 0.5f;
	int clientCenterY = (overlayRect.bottom - overlayRect.top) * 0.5f;

	/*cout << "整个窗口.顶 = " << marginRect.top << endl;
	cout << "整个窗口.底 = " << marginRect.bottom << endl;
	cout << "整个窗口.左 = " << marginRect.left << endl;
	cout << "整个窗口.右 = " << marginRect.right << endl;
	cout << "客户区窗口.顶 = " << overlayRect.top << endl;
	cout << "客户区窗口.底 = " << overlayRect.bottom << endl;
	cout << "客户区窗口.左 = " << overlayRect.left << endl;
	cout << "客户区窗口.右 = " << overlayRect.right << endl;
	cout << "winBorderLeft = " << winBorderLeft << endl;
	cout << "winBorderTop = " << winBorderTop << endl;*/

	// 计算绘制窗口区域
	GlobalVars::get().drawRect = {
		marginRect.left + winBorderLeft,
		marginRect.top + winBorderTop,
		overlayRect.right - overlayRect.left,
		overlayRect.bottom - overlayRect.top,
		clientCenterX,
		clientCenterY
	};

	/*cout << "GlobalVars::get().drawRect.x = " << GlobalVars::get().drawRect.x << endl;
	cout << "GlobalVars::get().drawRect.y = " << GlobalVars::get().drawRect.y << endl;
	cout << "GlobalVars::get().drawRect.width = " << GlobalVars::get().drawRect.width << endl;
	cout << "GlobalVars::get().drawRect.height = " << GlobalVars::get().drawRect.height << endl;*/
}

// 初始化绘制层的窗口
void initOverlayWindow()
{
	auto hInstance = GetModuleHandle(NULL);
	WNDCLASSEX wc;
	wc = { 0 };
	wc.cbSize = sizeof(wc);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = HBRUSH(RGB(0, 0, 0));
	wc.lpszClassName = OVERLAY_WIN_CLASS;
	wc.hInstance = hInstance;
	wc.hIconSm = NULL;

	// 注册窗体类
	if (!RegisterClassEx(&wc))
	{
		MessageBox(NULL, "注册窗口类失败", "错误提示", MB_OK | MB_ICONERROR);
		return;
	}

	// 获取游戏窗口尺寸
	getGameWindowRect();

	// WS_EX_LAYERED 可使用SetLayeredWindowAttributes函数
	// WS_EX_TRANSPARENT 鼠标会穿透
	// WS_EX_TOPMOST 窗口置顶
	// WS_EX_TOOLWINDOW 任务栏不显示
	// 创建外部绘制用的窗口
	GlobalVars::get().overlayHWindow = CreateWindowEx(
		WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
		OVERLAY_WIN_CLASS,
		OVERLAY_WIN_NAME,
		WS_POPUP | WS_VISIBLE,
		GlobalVars::get().drawRect.x,
		GlobalVars::get().drawRect.y,
		GlobalVars::get().drawRect.width,
		GlobalVars::get().drawRect.height,
		NULL,
		NULL,
		wc.hInstance,
		NULL
	);

	if (GlobalVars::get().overlayHWindow == NULL)
	{
		MessageBox(NULL, "窗口创建失败", "错误提示", MB_OK | MB_ICONERROR);
		return;
	}
	cout << "创建绘制窗口成功 " << endl;

	// 初始化d3d设备
	if (!CreateDeviceD3D())
	{
		//失败的清理
		CleanupDeviceD3D();
		//销毁窗口类
		UnregisterClass(wc.lpszClassName, wc.hInstance);
		MessageBox(NULL, "D3D创建失败", "错误提示", MB_OK | MB_ICONERROR);
		return;
	}

	// 显示窗口
	ShowWindow(GlobalVars::get().overlayHWindow, SW_SHOW);
	// 更新窗体-发送WM_PAINT消息
	UpdateWindow(GlobalVars::get().overlayHWindow);
	// 关键色过滤，要和清空屏幕的颜色一致
	SetLayeredWindowAttributes(GlobalVars::get().overlayHWindow, RGB(0, 0, 0), 255, LWA_ALPHA);
	// 使窗口边框向客户区扩展，也就是距离客户区的4边的边距。4个值为-1，则扩展到整个客户区
	MARGINS marg = { -1, -1, -1, -1 };
	DwmExtendFrameIntoClientArea(GlobalVars::get().overlayHWindow, &marg);

	// imgui窗口透明，也可以解决D3D绘制锯齿
	DWM_BLURBEHIND bb = { 0 };
	HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
	bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
	bb.hRgnBlur = hRgn;
	bb.fEnable = TRUE;
	DwmEnableBlurBehindWindow(GlobalVars::get().overlayHWindow, &bb);

	MSG msg;
	ZeroMemory(&msg, sizeof(msg));
	while (msg.message != WM_QUIT)
	{
		// 轮询和处理消息
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			continue;
		}

		GlobalVars::get().hWindow = FindWindow(GAME_WIN_CLASS, GAME_WIN_NAME);
		if (GlobalVars::get().hWindow == NULL)
		{
			// 游戏窗口关闭，跳出循环
			break;
		}

		// 游戏窗口或绘制窗口没在最前端，暂停绘制
		HWND hwnd = GetForegroundWindow();
		if (GlobalVars::get().hWindow == hwnd || GlobalVars::get().overlayHWindow == hwnd)
		{
			// 使辅助窗口一直盖在游戏窗口上，这段代码需要降低执行频率
			getGameWindowRect();
			SetWindowPos(GlobalVars::get().overlayHWindow, HWND_TOPMOST, GlobalVars::get().drawRect.x, GlobalVars::get().drawRect.y, GlobalVars::get().drawRect.width, GlobalVars::get().drawRect.height, SWP_SHOWWINDOW);
			MoveWindow(GlobalVars::get().overlayHWindow, GlobalVars::get().drawRect.x, GlobalVars::get().drawRect.y, GlobalVars::get().drawRect.width, GlobalVars::get().drawRect.height, true);

			// 更新玩家信息
			GlobalVars::get().updatePlayerList();
			// 绘制开始
			Renderer::get().drawFrames();
		}
		//Sleep(1);
	}

	// 清理工作
	ImGui_ImplDX11_Shutdown();
	ImGui_ImplWin32_Shutdown();
	ImGui::DestroyContext();

	CleanupDeviceD3D();
	DestroyWindow(GlobalVars::get().overlayHWindow);
	UnregisterClass(wc.lpszClassName, wc.hInstance);
}

void init()
{
	// 获取游戏窗口句柄
	GlobalVars::get().hWindow = FindWindow(GAME_WIN_CLASS, GAME_WIN_NAME);
	if (GlobalVars::get().hWindow == NULL)
	{
		return;
	}

	// 获取进程ID/句柄和模块基址
	GlobalVars::get().pId = Memory::get().getProcessId(PROCESS_NAME);
	GlobalVars::get().hProcess = Memory::get().getProcessHandle(GlobalVars::get().pId);
	GlobalVars::get().baseAddr = Memory::get().GetModuleBaseAddr(MODULE_NAME);

	// 获取世界地址
	GlobalVars::get().worldAddr = Memory::get().read<uintptr_t>(GlobalVars::get().baseAddr + GlobalVars::get().ofs.world);

	// 获取矩阵地址
	uintptr_t viewMatrixBaseAddr = Memory::get().read<uintptr_t>(GlobalVars::get().baseAddr + GlobalVars::get().ofs.viewMatrix);
	uintptr_t viewMatrixBaseAddr_offset1 = Memory::get().read<uintptr_t>(viewMatrixBaseAddr + GlobalVars::get().ofs.viewMatrix_offset1);
	GlobalVars::get().viewMatrixAddr = viewMatrixBaseAddr_offset1 + GlobalVars::get().ofs.viewMatrix_offset2;

	/*cout << "游戏进程ID = " << GlobalVars::get().pId << endl;
	cout << "游戏进程句柄 = " << GlobalVars::get().hProcess << endl;
	cout << "游戏窗口句柄 = " << GlobalVars::get().hWindow << endl;
	cout << "游戏模块基址 = " << GlobalVars::get().baseAddr << endl;
	cout << "世界地址 = " << GlobalVars::get().worldAddr << endl;
	cout << "矩阵地址 = " << GlobalVars::get().viewMatrixAddr << endl;*/

	initOverlayWindow();
}

Cheater::Cheater()
{
	// 测试游戏是：吃鸡模拟器 BattleRoyaleTrainerV1.0.1.8_chs
	init();
}
